package com.yunchao.library.lock

import java.util.Calendar
import kotlin.random.Random

// 常量定义
private const val PAGE_NUMBER_LENGTH = 4
private const val COMMON_LENGTH = 8
private const val DEVICE_ID_LENGTH = 5

/**
 * 创建锁指令
 *
 * @param lockAction 指令
 * @param epcStr EPC字符串
 * @param deviceId 设备ID
 * @param operatorId 操作员ID
 * @param chkUserId 校验用户ID
 * @param pageNumber 页数
 * @return 生成的锁指令字符串
 */
private fun createLockCmdStr(
    lockAction: LockAction,
    epcStr: String,
    deviceId: Int,
    operatorId: Int,
    chkUserId: Int,
    pageNumber: Int
): String = StringBuilder(lockAction.cmd).apply {
    append(epcStr)
    append("16")
    append(decimalToHex(pageNumber, PAGE_NUMBER_LENGTH))
    append(decimalToHex(operatorId, COMMON_LENGTH))
    append(decimalToHex(chkUserId, COMMON_LENGTH))
    append(decimalToHex(getCurrentTimeBinary().toInt(2), COMMON_LENGTH))
    append(decimalToHex(deviceId, DEVICE_ID_LENGTH))
    append("0")
    append(decimalToHex(generateRandomNumberWithNonZeroDigits(), COMMON_LENGTH))
    append("00")
}.toString().uppercase()

// 闭锁物流指令
fun getCloseLockLogisticsCmdStr(
    epcStr: String,
    deviceId: Int,
    operatorId: Int,
    chkUserId: Int,
    pageNumber: Int
): String = createLockCmdStr(
    LockAction.CLOSE_LOCK_LOGISTICS,
    epcStr, deviceId, operatorId, chkUserId, pageNumber
)

// 开锁物流指令
fun getOpenLockLogisticsCmdStr(
    epcStr: String,
    deviceId: Int,
    operatorId: Int,
    chkUserId: Int,
    pageNumber: Int
): String = createLockCmdStr(
    LockAction.OPEN_LOCK_LOGISTICS,
    epcStr, deviceId, operatorId, chkUserId, pageNumber
)

// 闭锁指令
fun getCloseLockCmdStr(
    epcStr: String, deviceId: Int,
    operatorId: Int,
    chkUserId: Int,
    pageNumber: Int
): String = createLockCmdStr(
    LockAction.CLOSE_LOCK,
    epcStr, deviceId, operatorId, chkUserId, pageNumber
)

// 开锁指令
fun getOpenLockCmdStr(
    epcStr: String, deviceId: Int,
    operatorId: Int,
    chkUserId: Int,
    pageNumber: Int
): String = createLockCmdStr(
    LockAction.OPEN_LOCK,
    epcStr, deviceId, operatorId, chkUserId, pageNumber
)

// 恢复锁指令
fun getRecoveryLockCmdStr(
    epcStr: String,
    deviceId: Int,
    operatorId: Int,
    chkUserId: Int,
    pageNumber: Int
): String = createLockCmdStr(
    LockAction.RECOVERY_LOCK,
    epcStr, deviceId, operatorId, chkUserId, pageNumber
)

// 清空锁指令
fun getClearLockCmdStr(
    epcStr: String,
    deviceId: Int,
    operatorId: Int,
    chkUserId: Int
): String = StringBuilder(LockAction.CLEAR_LOCK_LOG.cmd).apply {
    append(epcStr)
    append("13")
    append(decimalToHex(operatorId, COMMON_LENGTH))
    append(decimalToHex(chkUserId, COMMON_LENGTH))
    append(decimalToHex(getCurrentTimeBinary().toInt(2), COMMON_LENGTH))
    append(decimalToHex(deviceId, DEVICE_ID_LENGTH))
    append("0")
    append(decimalToHex(generateRandomNumberWithNonZeroDigits(), COMMON_LENGTH))
}.toString().uppercase()

// 追溯指令
fun getLockLogCmdStr(epcStr: String, number: Int, termNumber: Int): String =
    StringBuilder(LockAction.LOCK_LOG.cmd).apply {
        append(epcStr)
        append("04")
        append(decimalToHex(number, 4))
        append(decimalToHex(termNumber, 2))
        append("00")
    }.toString().uppercase()

// 锁密码指令
fun getReadWritePwd(epcStr: String): String = StringBuilder(LockAction.PASSWORD_LOCK.cmd).apply {
    append(epcStr)
    append("00")
}.toString().uppercase()

// 激活锁指令
fun getActivateLockCmdStr(epcStr: String, voucherType: String): String =
    StringBuilder(LockAction.ACTIVATE_LOCK.cmd).apply {
        append(epcStr)
        append("0E")
        append(decimalToHex(voucherType.toInt(2), 1))
        append(decimalToHex(0, COMMON_LENGTH))
        append(decimalToHex(0, COMMON_LENGTH))
        append(decimalToHex(0, 16))
        append("00")
    }.toString().uppercase()

// 验证锁EPC信息指令
fun getAuthLockEPC(epcStr: String, epcTid: String): String =
    StringBuilder(LockAction.AUTH_LOCK.cmd).apply {
        append(epcStr)
        append("0C")
        append(epcTid)
    }.toString().uppercase()

// 锁日志解密指令
fun getEncryptCmd(strData: String, strMsgLog: String): String {
    val totalLength = (strData.length + strMsgLog.length) / 2 + 2
    val logLength = strMsgLog.length / 2

    return StringBuilder(LockAction.DECRYPTION_LOCK.cmd).apply {
        append(Integer.toHexString(totalLength).uppercase())
        append("0C")
        append(strData)
        append(Integer.toHexString(logLength).uppercase())
        append(strMsgLog)
        append("00")
    }.toString().uppercase()
}

// 十六进制字符串转换为字节数组
fun hexStringToBytes(hexString: String): ByteArray {
    return hexString.lowercase().chunked(2)
        .map { it.toIntOrNull(16)?.toByte() ?: 0 }
        .toByteArray()
}

// 字节数组转换为十六进制字符串
fun bytesToHexString(array: ByteArray): String {
    return array.joinToString("") { "%02X".format(it.toInt() and 0xFF) }
}

// 获取日期编码
fun getDate(year: Int, month: Int, day: Int): Int {
    require(year > 1980) { "Invalid year: $year" }
    require(month in 1..12) { "Invalid month: $month" }
    require(day in 1..31) { "Invalid day: $day" }
    return (((year - 1980) shl 9).plus(month shl 5)).plus(day)
}

// 获取时间编码
fun getTime(hour: Int, minute: Int, second: Int): Int {
    require(hour in 0..23) { "Invalid hour: $hour" }
    require(minute in 0..59) { "Invalid minute: $minute" }
    require(second in 0..59) { "Invalid second: $second" }
    return (hour shl 11).plus(minute shl 5).plus(second shr 1)
}

// 解码日期
fun getDateComponents(encodedDate: Int): Triple<Int, Int, Int> {
    val year = ((encodedDate shr 9) and 0x7FFF) + 1980
    val month = (encodedDate shr 5) and 0x0F
    val day = encodedDate and 0x1F
    return Triple(year, month, day)
}

// 解码时间
fun getTimeComponents(encodedTime: Int): Triple<Int, Int, Int> {
    val hour = encodedTime shr 11
    val minute = (encodedTime shr 5) and 0x3F
    val second = (encodedTime shl 1) and 0x3F
    return Triple(hour, minute, second)
}


private fun getCurrentTimeBinary(): String {
    val cal = Calendar.getInstance()
    val year = cal[Calendar.YEAR] % 100
    val month = cal[Calendar.MONTH] + 1
    val day = cal[Calendar.DAY_OF_MONTH]
    val hour = cal[Calendar.HOUR_OF_DAY]
    val minute = cal[Calendar.MINUTE]
    val second = cal[Calendar.SECOND]
    return decimalToBinaryWithPadding(year, 6) +
            decimalToBinaryWithPadding(month, 4) +
            decimalToBinaryWithPadding(day, 5) +
            decimalToBinaryWithPadding(hour, 5) +
            decimalToBinaryWithPadding(minute, 6) +
            decimalToBinaryWithPadding(second, 6)
}

fun getOperatorTime(str: String): String {
    val year = str.substring(0, 6).toInt(2) + 2000
    val month = str.substring(6, 10).toInt(2).toString().padStart(2, '0')
    val day = str.substring(10, 15).toInt(2).toString().padStart(2, '0')
    val hour = str.substring(15, 20).toInt(2).toString().padStart(2, '0')
    val minute = str.substring(20, 26).toInt(2).toString().padStart(2, '0')
    val second = str.substring(26).toInt(2).toString().padStart(2, '0')
    return "$year-$month-$day $hour:$minute:$second"
}

fun hexToBinary(hex: String): String = hex.toInt(16).toString(2)

private fun decimalToBinaryWithPadding(decimal: Int, length: Int): String =
    decimal.toString(2).padStart(length, '0')

private fun decimalToHex(number: Int, length: Int): String =
    number.toString(16).uppercase().padStart(length, '0')

private fun generateRandomNumberWithNonZeroDigits(): Int {
    var number: Int
    do {
        number = Random.nextInt(0, 100000000)
    } while (number.toString().contains('0'))
    return number
}